#!/usr/bin/env ruby

require 'logger'
require 'rest_client'
require 'rexml/document'
include REXML

$stdout.sync = true
$stdin.sync = true

path = "/var/log/ejabberd/auth.log"
file = File.open(path, File::WRONLY | File::APPEND | File::CREAT)
file.sync = true
$logger = Logger.new(file)
$logger.level = Logger::DEBUG

$logger.info "#{Process.pid}: Starting ejabberd authentication service"


def getOption(option)
  File.open('/etc/ejabberd/ssconfig.cfg', 'r') do |f1|  
    while line = f1.gets  
      line = line.gsub(/\n/,'')
      if line.match(/^#/)
        #Comments
      elsif line.match(/^#{option}/)
        return line.gsub(/#{option}/,'')
      end  
    end  
  end
  return "Undefined"
end

$cookie_name = getOption("cookie_name=")
$force_ssl = getOption("force_ssl=")

if $force_ssl=="true"
	$http="https"
else
	$http="http"
end


def auth(username,domain,password)

  #[TEST ONLY] Allow everybody
  #return true

  #[TEST] Admin password
  #if username == "admin" and password == "pass"
  #  return true
  #end

  accessByPasswordUrl = $http + "://" + getWebDomainUrlFromDomain(domain) + "/users/sign_in"

  begin
    response = RestClient.post accessByPasswordUrl, :user => { :email => username , :password => password }

    return response.code == 201

  rescue => e
    if e.class.name == "RestClient::Found" &&
      e.http_code == 302 &&
      e.http_body =~ /home/
      return true
    end

    $logger.error "#{Process.pid}: Exception in auth(username, password)"
    $logger.error "#{Process.pid}: #{e.class.name}: #{e.message}"

    return false
  end
end


def authByCookie(username, domain, cookie)
  begin
    accessByCookieUrl = $http + "://" + getWebDomainUrlFromDomain(domain) + "/api/me"
    response = RestClient.get accessByCookieUrl, :cookies => {:"#{$cookie_name}" => cookie}
    doc = REXML::Document.new(response.body)

    slug = ""
    doc.elements.each('user/slug') do |ele|
      slug = ele.text
    end

    if username != "" and username == slug
	return true
    else
	return false
    end  

  rescue => e
    
    unless e.class.name == "RestClient::Unauthorized" and e.message == "401 Unauthorized"
	$logger.error "#{Process.pid}: Exception in authByCookie(username, cookie)"
    	$logger.error "#{Process.pid}: #{e.class.name}: #{e.message}"
    end

    return false
  end
end


def validateParameters(username,domain,password)
	if !username or !password or !domain
		return false
	end
	if (username.gsub(/\s+/, "")=="") or (password.gsub(/\s+/, "")=="") or (domain.gsub(/\s+/, "")=="")
		return false
	end
	return true
end


def getWebDomainUrlFromDomain(domain)
	web_domain = getOption(domain + "=");
	if (web_domain != "Undefined")
		return web_domain
	else
		return domain
	end
end


loop do
  begin
    $stdin.eof? # wait for input
    start = Time.now

    msg = $stdin.read(2)
    length = msg.unpack('n').first

    msg = $stdin.read(length)
    cmd, *data = msg.split(":")

    $logger.info "#{Process.pid}: Incoming Request: '#{cmd}'"
    success = case cmd
    
    when "auth"
      $logger.info "#{Process.pid}: Authenticating #{data[0]}@#{data[1]}"
      
      #Parameters basic validation: validateParameters(username,domain,password)
      if !validateParameters(data[0],data[1],data[2])
	$logger.info "#{Process.pid}: Invalid parameters"	
	return false
      end

      #Select authorization condition for LOGIN
      #Authentication methods: user/password or user/cookie
      password = data[2]	
      if password.split(">>")[0]=="AuthenticationByCookie"
	cookie = password.split(">>")[1]
	$logger.info "#{Process.pid}: With userJid #{data[0]}@#{data[1]} and cookie #{cookie}"      	
	authByCookie(data[0], data[1], cookie)
      else
	$logger.info "#{Process.pid}: With userJid #{data[0]}@#{data[1]} and password ******"
	#$logger.info "#{Process.pid}: With userJid #{data[0]}@#{data[1]} and password #{data[2]}"
      	auth(data[0], data[1], data[2])
      end
      
    when "isuser"
     
      $logger.info "#{Process.pid}: Isuser with userJid: #{data[0]}@#{data[1]}"

      #Authorization condition for ISUSER (Add buddys)
      true
      
    else
      false
    end

    bool = success ? 1 : 0
    $stdout.write [2, bool].pack("nn")
    $logger.info "#{Process.pid}: Response: #{success ? "success" : "failure"}"
  rescue => e
    $logger.error "#{Process.pid}: #{e.class.name}: #{e.message}"
    $logger.error "#{Process.pid}: " + e.backtrace.join("\n\t")
    $logger.error "#{Process.pid}: Finish process"
    break
  end
end
